﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace Utils.MathUtilitys
{
    public static class MathUtils
    {
        /*
         * Length from center to corner of a quadrat with the width 1
         */
        public const float QUADRAT_DIAGONAL = 0.707f;

        public const float PI = (float)Math.PI;
        public const float PI2 = (float)Math.PI * 2;
        public const float PIHalf = (float)Math.PI / 2f;
        public const float PIInversed = 1 / (float)Math.PI;
        public const float DegreeToRad = (float)Math.PI / 180f;
        public const float RadToDegree = 180f / (float)Math.PI;

        public static float Cap(float val, float min, float max)
        {
            if (val < min) return min;
            if (val > max) return max;
            return val;
        }

        public static int Cap(int val, RangeValue<int> range)
        {
            if (val < range.Min) return range.Min;
            if (val > range.Max) return range.Max;
            return val;
        }

        public static int Cap(int val, int min, int max)
        {
            if (val < min) return min;
            if (val > max) return max;
            return val;
        }

        public static float Cap(float val, RangeValue<float> range)
        {
            if (val < range.Min) return range.Min;
            if (val > range.Max) return range.Max;
            return val;
        }

        public static float CapCyclonic(float val, float min, float max)
        {
            float dif = max - min;

            Debug.Assert(dif >= 0, "max is not bigger then min");

            while (val < min)
            {
                val += dif;
            }

            while (val > max)
            {
                val -= dif;
            }

            return val;
        }

        public static float CapCyclonic(float val, RangeValue<float> range)
        {
            return CapCyclonic(val, range.Min, range.Max);
        }

        public static float CircleFunction(float val)
        {
            // a Function that describes one half of a Circle, val should be between 0 and 1.
            // original function: y = sqrt(1 - x²) with x -1 to 1.
            return (float)Math.Sqrt(1 - Sqr(Cap(val, 0, 1) * 2 - 1));
        }

        public static float Sqr(float val)
        {
            return val * val;
        }

        public static float Sqrt(float val)
        {
            return (float)Math.Sqrt(val);
        }

        public static float Range(float min, float max, float per)
        {
            return min + (max - min) * per;
        }

        public static float RangeSmooth(float min, float max, float per)
        {
            float realPer = (1 - MathUtils.FCos(per * MathUtils.PI))/2;
            return min + (max - min) * realPer;
        }

        public static float RangeAccelerating(float min, float max, float per)
        {
            float realPer = MathUtils.Sqr(per);
            return min + (max - min) * realPer;
        }

        public static float RangeDecelerating(float min, float max, float per)
        {
            float realPer = 1 - MathUtils.Sqr(1 - per);
            return min + (max - min) * realPer;
        }

        public static float Range(RangeValue<float> range, float per)
        {
            return range.Min + (range.Max - range.Min) * per;
        }

        public static float RangeSmooth(RangeValue<float> range, float per)
        {
            return RangeSmooth(range.Min, range.Max, per);
        }

        public static float RangeAccelerating(RangeValue<float> range, float per)
        {
            return RangeAccelerating(range.Min, range.Max, per);
        }

        public static float RangeDecelerating(RangeValue<float> range, float per)
        {
            return RangeDecelerating(range.Min, range.Max, per);
        }

        public static float Abs(float val)
        {
            if (val >= 0)
            {
                return val;
            }
            else
            {
                return -val;
            }
        }

        public static int Sign(float val)
        {
            if (val > 0)
            {
                return 1;
            }
            else if (val < 0)
            {
                return -1;
            }
            else
            {
                return 0;
            }
        }

        public static float Min(float val0, float val1)
        {
            if (val0 < val1)
            {
                return val0;
            }
            return val1;
        }

        public static float Max(float val0, float val1)
        {
            if (val0 > val1)
            {
                return val0;
            }
            return val1;
        }

        public static float FSin(float rad)
        {
            return (float)Math.Sin(rad);
        }

        public static float FCos(float rad)
        {
            return (float)Math.Cos(rad);
        }

        public static float FTan(float val)
        {
            return (float)Math.Tan(val);
        }

        public static float FAsin(float val)
        {
            return (float)Math.Asin(val);
        }

        public static float FAcos(float val)
        {
            return (float)Math.Acos(val);
        }

        public static float FAtan(float val)
        {
            return (float)Math.Atan(val);
        }

        public static float FAtan2(float val1, float val2)
        {
            return (float)Math.Atan2(val1, val2);
        }
    }
}
